<?php

namespace WebmanGrpc\Event;

/**
 * 事件总线
 * 
 * 提供事件的发布和订阅机制
 */
class EventBus
{
    /**
     * 单例实例
     * @var EventBus|null
     */
    protected static ?EventBus $instance = null;
    
    /**
     * 事件监听器
     * @var array
     */
    protected array $listeners = [];
    
    /**
     * 全局监听器
     * @var array
     */
    protected array $globalListeners = [];
    
    /**
     * 事件历史记录
     * @var array
     */
    protected array $eventHistory = [];
    
    /**
     * 最大历史记录数
     * @var int
     */
    protected int $maxHistorySize = 100;
    
    /**
     * 是否启用事件历史记录
     * @var bool
     */
    protected bool $historyEnabled = false;
    
    /**
     * 获取单例实例
     * 
     * @return EventBus 事件总线实例
     */
    public static function getInstance(): EventBus
    {
        if (self::$instance === null) {
            self::$instance = new self();
        }
        
        return self::$instance;
    }
    
    /**
     * 私有构造函数，防止直接实例化
     */
    protected function __construct()
    {
        // 初始化配置
    }
    
    /**
     * 克隆方法，防止克隆
     */
    private function __clone()
    {
        // 防止克隆
    }
    
    /**
     * 添加事件监听器
     * 
     * @param string $eventName 事件名称
     * @param EventListenerInterface|callable $listener 监听器
     * @param int $priority 优先级
     * @return void
     */
    public function addListener(string $eventName, $listener, int $priority = 0): void
    {
        if (!isset($this->listeners[$eventName])) {
            $this->listeners[$eventName] = [];
        }
        
        $this->listeners[$eventName][] = [
            'listener' => $listener,
            'priority' => $priority,
        ];
        
        // 按优先级排序
        usort($this->listeners[$eventName], function ($a, $b) {
            return $b['priority'] - $a['priority'];
        });
    }
    
    /**
     * 移除事件监听器
     * 
     * @param string $eventName 事件名称
     * @param EventListenerInterface|callable $listener 监听器
     * @return void
     */
    public function removeListener(string $eventName, $listener): void
    {
        if (!isset($this->listeners[$eventName])) {
            return;
        }
        
        $this->listeners[$eventName] = array_filter($this->listeners[$eventName], function ($item) use ($listener) {
            return $item['listener'] !== $listener;
        });
    }
    
    /**
     * 添加全局事件监听器
     * 
     * @param EventListenerInterface|callable $listener 监听器
     * @param int $priority 优先级
     * @return void
     */
    public function addGlobalListener($listener, int $priority = 0): void
    {
        $this->globalListeners[] = [
            'listener' => $listener,
            'priority' => $priority,
        ];
        
        // 按优先级排序
        usort($this->globalListeners, function ($a, $b) {
            return $b['priority'] - $a['priority'];
        });
    }
    
    /**
     * 移除全局事件监听器
     * 
     * @param EventListenerInterface|callable $listener 监听器
     * @return void
     */
    public function removeGlobalListener($listener): void
    {
        $this->globalListeners = array_filter($this->globalListeners, function ($item) use ($listener) {
            return $item['listener'] !== $listener;
        });
    }
    
    /**
     * 发布事件
     * 
     * @param EventInterface $event 事件对象
     * @return EventInterface 处理后的事件对象
     */
    public function publish(EventInterface $event): EventInterface
    {
        // 记录事件历史
        if ($this->historyEnabled) {
            $this->recordEvent($event);
        }
        
        // 触发全局监听器
        foreach ($this->globalListeners as $item) {
            $this->invokeListener($item['listener'], $event);
            
            if ($event->isPropagationStopped()) {
                break;
            }
        }
        
        // 触发特定事件的监听器
        $eventName = $event->getName();
        if (isset($this->listeners[$eventName])) {
            foreach ($this->listeners[$eventName] as $item) {
                $this->invokeListener($item['listener'], $event);
                
                if ($event->isPropagationStopped()) {
                    break;
                }
            }
        }
        
        return $event;
    }
    
    /**
     * 调用监听器
     * 
     * @param EventListenerInterface|callable $listener 监听器
     * @param EventInterface $event 事件对象
     * @return void
     */
    protected function invokeListener($listener, EventInterface $event): void
    {
        try {
            if ($listener instanceof EventListenerInterface) {
                $listener->handle($event);
            } elseif (is_callable($listener)) {
                $listener($event);
            }
        } catch (\Exception $e) {
            // 忽略监听器异常，避免影响其他监听器
            error_log("Event listener error: " . $e->getMessage());
        }
    }
    
    /**
     * 记录事件历史
     * 
     * @param EventInterface $event 事件对象
     * @return void
     */
    protected function recordEvent(EventInterface $event): void
    {
        $this->eventHistory[] = [
            'name' => $event->getName(),
            'data' => $event->getData(),
            'timestamp' => $event->getTimestamp(),
        ];
        
        // 限制历史记录大小
        if (count($this->eventHistory) > $this->maxHistorySize) {
            array_shift($this->eventHistory);
        }
    }
    
    /**
     * 获取事件历史
     * 
     * @param string|null $eventName 事件名称，为null时返回所有事件
     * @param int $limit 返回记录数限制
     * @return array 事件历史
     */
    public function getEventHistory(?string $eventName = null, int $limit = 0): array
    {
        if ($eventName === null) {
            $history = $this->eventHistory;
        } else {
            $history = array_filter($this->eventHistory, function ($event) use ($eventName) {
                return $event['name'] === $eventName;
            });
        }
        
        if ($limit > 0) {
            $history = array_slice($history, -$limit);
        }
        
        return array_values($history);
    }
    
    /**
     * 清空事件历史
     * 
     * @return void
     */
    public function clearEventHistory(): void
    {
        $this->eventHistory = [];
    }
    
    /**
     * 启用事件历史记录
     * 
     * @param int $maxHistorySize 最大历史记录数
     * @return void
     */
    public function enableHistory(int $maxHistorySize = 100): void
    {
        $this->historyEnabled = true;
        $this->maxHistorySize = $maxHistorySize;
    }
    
    /**
     * 禁用事件历史记录
     * 
     * @return void
     */
    public function disableHistory(): void
    {
        $this->historyEnabled = false;
    }
    
    /**
     * 获取事件监听器
     * 
     * @param string|null $eventName 事件名称，为null时返回所有监听器
     * @return array 事件监听器
     */
    public function getListeners(?string $eventName = null): array
    {
        if ($eventName === null) {
            return $this->listeners;
        }
        
        return $this->listeners[$eventName] ?? [];
    }
    
    /**
     * 获取全局事件监听器
     * 
     * @return array 全局事件监听器
     */
    public function getGlobalListeners(): array
    {
        return $this->globalListeners;
    }
    
    /**
     * 检查是否有事件监听器
     * 
     * @param string|null $eventName 事件名称，为null时检查是否有任何监听器
     * @return bool 是否有监听器
     */
    public function hasListeners(?string $eventName = null): bool
    {
        if ($eventName === null) {
            return !empty($this->listeners) || !empty($this->globalListeners);
        }
        
        return !empty($this->listeners[$eventName]) || !empty($this->globalListeners);
    }
    
    /**
     * 清空所有监听器
     * 
     * @return void
     */
    public function clearAllListeners(): void
    {
        $this->listeners = [];
        $this->globalListeners = [];
    }
    
    /**
     * 获取事件总线统计信息
     * 
     * @return array 统计信息
     */
    public function getStats(): array
    {
        $listenerCount = 0;
        foreach ($this->listeners as $eventListeners) {
            $listenerCount += count($eventListeners);
        }
        
        return [
            'event_listeners' => $listenerCount,
            'global_listeners' => count($this->globalListeners),
            'event_types' => count($this->listeners),
            'history_enabled' => $this->historyEnabled,
            'history_size' => count($this->eventHistory),
            'max_history_size' => $this->maxHistorySize,
        ];
    }
}